#region Apache License
//
// Licensed to the Apache Software Foundation (ASF) under one or more 
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership. 
// The ASF licenses this file to you under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with 
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#endregion

// .NET Compact Framework 1.0 has no support for System.Runtime.Remoting
#if !NETCF

using log4net.Core;
using log4net.Repository;
using log4net.Util;
using System;
using System.Runtime.Remoting;
using IRemoteLoggingSink = log4net.Appender.RemotingAppender.IRemoteLoggingSink;

namespace log4net.Plugin
{
    /// <summary>
    /// Plugin that listens for events from the <see cref="log4net.Appender.RemotingAppender"/>
    /// </summary>
    /// <remarks>
    /// <para>
    /// This plugin publishes an instance of <see cref="IRemoteLoggingSink"/> 
    /// on a specified <see cref="SinkUri"/>. This listens for logging events delivered from
    /// a remote <see cref="log4net.Appender.RemotingAppender"/>.
    /// </para>
    /// <para>
    /// When an event is received it is relogged within the attached repository
    /// as if it had been raised locally.
    /// </para>
    /// </remarks>
    /// <author>Nicko Cadell</author>
    /// <author>Gert Driesen</author>
    public class RemoteLoggingServerPlugin : PluginSkeleton
    {
        #region Public Instance Constructors

        /// <summary>
        /// Default constructor
        /// </summary>
        /// <remarks>
        /// <para>
        /// Initializes a new instance of the <see cref="RemoteLoggingServerPlugin" /> class.
        /// </para>
        /// <para>
        /// The <see cref="SinkUri"/> property must be set.
        /// </para>
        /// </remarks>
        public RemoteLoggingServerPlugin() : base("RemoteLoggingServerPlugin:Unset URI")
        {
        }

        /// <summary>
        /// Construct with sink Uri.
        /// </summary>
        /// <param name="sinkUri">The name to publish the sink under in the remoting infrastructure. 
        /// See <see cref="SinkUri"/> for more details.</param>
        /// <remarks>
        /// <para>
        /// Initializes a new instance of the <see cref="RemoteLoggingServerPlugin" /> class
        /// with specified name.
        /// </para>
        /// </remarks>
        public RemoteLoggingServerPlugin(string sinkUri) : base("RemoteLoggingServerPlugin:" + sinkUri)
        {
            m_sinkUri = sinkUri;
        }

        #endregion Public Instance Constructors

        #region Public Instance Properties

        /// <summary>
        /// Gets or sets the URI of this sink.
        /// </summary>
        /// <value>
        /// The URI of this sink.
        /// </value>
        /// <remarks>
        /// <para>
        /// This is the name under which the object is marshaled.
        /// <see cref="RemotingServices.Marshal(MarshalByRefObject,String,Type)"/>
        /// </para>
        /// </remarks>
        public virtual string SinkUri
        {
            get { return m_sinkUri; }
            set { m_sinkUri = value; }
        }

        #endregion Public Instance Properties

        #region Override implementation of PluginSkeleton

        /// <summary>
        /// Attaches this plugin to a <see cref="ILoggerRepository"/>.
        /// </summary>
        /// <param name="repository">The <see cref="ILoggerRepository"/> that this plugin should be attached to.</param>
        /// <remarks>
        /// <para>
        /// A plugin may only be attached to a single repository.
        /// </para>
        /// <para>
        /// This method is called when the plugin is attached to the repository.
        /// </para>
        /// </remarks>
        override public void Attach(ILoggerRepository repository)
        {
            base.Attach(repository);

            // Create the sink and marshal it
            m_sink = new RemoteLoggingSinkImpl(repository);

            try
            {
                RemotingServices.Marshal(m_sink, m_sinkUri, typeof(IRemoteLoggingSink));
            }
            catch (Exception ex)
            {
                LogLog.Error(declaringType, "Failed to Marshal remoting sink", ex);
            }
        }

        /// <summary>
        /// Is called when the plugin is to shutdown.
        /// </summary>
        /// <remarks>
        /// <para>
        /// When the plugin is shutdown the remote logging
        /// sink is disconnected.
        /// </para>
        /// </remarks>
#if NET_4_0
        [System.Security.SecuritySafeCritical]
#endif
        override public void Shutdown()
        {
            // Stops the sink from receiving messages
            RemotingServices.Disconnect(m_sink);
            m_sink = null;

            base.Shutdown();
        }

        #endregion Override implementation of PluginSkeleton

        #region Private Instance Fields

        private RemoteLoggingSinkImpl m_sink;
        private string m_sinkUri;

        #endregion Private Instance Fields

        #region Private Static Fields

        /// <summary>
        /// The fully qualified type of the RemoteLoggingServerPlugin class.
        /// </summary>
        /// <remarks>
        /// Used by the internal logger to record the Type of the
        /// log message.
        /// </remarks>
        private readonly static Type declaringType = typeof(RemoteLoggingServerPlugin);

        #endregion Private Static Fields

        /// <summary>
        /// Delivers <see cref="LoggingEvent"/> objects to a remote sink.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Internal class used to listen for logging events
        /// and deliver them to the local repository.
        /// </para>
        /// </remarks>
        private class RemoteLoggingSinkImpl : MarshalByRefObject, IRemoteLoggingSink
        {
            #region Public Instance Constructors

            /// <summary>
            /// Constructor
            /// </summary>
            /// <param name="repository">The repository to log to.</param>
            /// <remarks>
            /// <para>
            /// Initializes a new instance of the <see cref="RemoteLoggingSinkImpl"/> for the
            /// specified <see cref="ILoggerRepository"/>.
            /// </para>
            /// </remarks>
            public RemoteLoggingSinkImpl(ILoggerRepository repository)
            {
                m_repository = repository;
            }

            #endregion Public Instance Constructors

            #region Implementation of IRemoteLoggingSink

            /// <summary>
            /// Logs the events to the repository.
            /// </summary>
            /// <param name="events">The events to log.</param>
            /// <remarks>
            /// <para>
            /// The events passed are logged to the <see cref="ILoggerRepository"/>
            /// </para>
            /// </remarks>
            public void LogEvents(LoggingEvent[] events)
            {
                if (events != null)
                {
                    foreach (LoggingEvent logEvent in events)
                    {
                        if (logEvent != null)
                        {
                            m_repository.Log(logEvent);
                        }
                    }
                }
            }

            #endregion Implementation of IRemoteLoggingSink

            #region Override implementation of MarshalByRefObject

            /// <summary>
            /// Obtains a lifetime service object to control the lifetime 
            /// policy for this instance.
            /// </summary>
            /// <returns><c>null</c> to indicate that this instance should live forever.</returns>
            /// <remarks>
            /// <para>
            /// Obtains a lifetime service object to control the lifetime 
            /// policy for this instance. This object should live forever
            /// therefore this implementation returns <c>null</c>.
            /// </para>
            /// </remarks>
#if NET_4_0
            [System.Security.SecurityCritical]
#endif
            public override object InitializeLifetimeService()
            {
                return null;
            }

            #endregion Override implementation of MarshalByRefObject

            #region Private Instance Fields

            /// <summary>
            /// The underlying <see cref="ILoggerRepository" /> that events should
            /// be logged to.
            /// </summary>
            private readonly ILoggerRepository m_repository;

            #endregion Private Instance Fields
        }
    }
}

#endif // !NETCF